module xenWindow;

import xenocide;
import xenGameState;
import tango.util.container.LinkedList;

T min(T) (T a, T b)
{
     return (a < b) ? a : b;
}

T max(T) (T a, T b)
{
     return (a > b) ? a : b;
}


class SubWindow
{
	int [2] position, size, screenCenter;
	GameState gameState;


	this(GameState iGS,int[] iPos,int [] iSize)
	{
        position[]=iPos;
        size[]=iSize;
        gameState=iGS;
        screenCenter[0]=cast(int)(iSize[0]/2);
		screenCenter[1]=cast(int)(iSize[1]/2);
		
	}
	void draw(float dt)
	{
		
	}
	
	void zoomInOut(int i)
	{
	}
    int[] isScreenPointIn(int[] iPos)
	{
        int x=iPos[0]-position[0];
		int y=iPos[1]-position[1];
        int w=size[0];
		int h=size[1];
        
        if ((0<=x)&&(x<w)&&(0<=y)&&(y<h))
		{
            return [x,y];
		}
		return [-1,-1];
    }

    float[] getRelativeScreenCoord(int [2] iPt)
	{
        auto relPos=isScreenPointIn(iPt);
        if (relPos==[-1,-1])
		{
            //print "error, point outside window"
            return [-1f,-1f];
        }
        
        float u = (cast(float)relPos[0]) / cast(float)(size[0]);
        float v = (cast(float)relPos[1]) / cast(float)(size[1]);
        return [u,v];
	}
    void prepareView()
	{
        /*
        Updates the view offset based on the center of the screen.

        Recalculates the necessary projection.
        */
        glViewport(position[0],position[1], size[0],size[1]);
        
        glMatrixMode(GL_PROJECTION);
        glLoadIdentity();

        
        gluOrtho2D(0,size[0],0,size[1]);
        
        glMatrixMode(GL_MODELVIEW);
        glLoadIdentity();
    }
	int on_mouse_move(int x,int y,int xrel,int yrel,int button,int shift,int ctrl,int alt)
	{
		return 0;
	}
	int on_mouse_press(int x,int y,int button,int shift,int ctrl,int alt)
	{
		return 0;
	}
	int on_mouse_release(int x,int y,int button,int shift,int ctrl,int alt)
	{
		return 0;
	}
	int on_key_press(int key,bool shift,bool ctrl,bool alt)
	{
		switch (key)
		{
			default:
			break;
		}
		return 0;	
	}
	int on_key_release(int key,bool shift,bool ctrl,bool alt)
	{
		return 0;
	}
    /*
	def on_mouse_press( x, y, button, modifiers):
        rc=isScreenPointIn((x,y))
        print "mouse press"
        if not rc:
            print "Outside bounds"
            return False
        return False
    
    def on_mouse_release( x, y, button, modifiers):
        rc=isScreenPointIn((x,y))
        if not rc:
            return False
        return False
    
    def on_mouse_scroll( x, y, scroll_x, scroll_y):
        rc=isScreenPointIn((x,y))
        if not rc:
            return False
        return False
    
    def on_mouse_drag( x, y, dx, dy, buttons, modifiers):
        rc=isScreenPointIn((x,y))
        if not rc:
            return False
        return False
	*/
}

class SubWindow3D:SubWindow
{
	float [2] viewCenter,extents,lower,upper,view,viewOffset,mouseWorld;
	float zoom,viewFactor,ratio;
	int boxMask=0x3F3F;
	float selectionBox[2][2];
	bool selBoxActive=false;
	int selectionType=1;
	enum SELECTION {LEFT=1,RIGHT};
	float mouseWorldInitialPos[2];
	
	xenGS xGS;

	
	this(xenGS iGS,int[2] iPos,int[2] iSize,float [2] iCenter,float iZoom)
	{
        super(iGS,iPos,iSize);
        xGS=iGS;
        viewCenter[]=[iCenter[0],iCenter[1]];
        zoom=iZoom;
        viewFactor=1f;
        
		float ax,ay,bx,by;
		ax=0.0f;
		ay=0.0f;
		bx=0.5f;
		by=0.7f;
		mouseWorldInitialPos[]=selectionBox[0][]=[ax,ay];
		selectionBox[1][]=[bx,by];
		updateSelBoxDL();
        //selectionBox=None
		viewFactor=1f;
        calculateExtents();

		glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
		
		

    }   

	void updateSelBoxDL()
	{
		glNewList(xenGS.DL.SelBox,GL_COMPILE);
		glBegin(GL_LINE_LOOP);
			if (selectionType==SELECTION.LEFT)
				glColor3f (1,    0.8,    0);
			else
				glColor3f (0.5,    1,    0);
			
			glVertex2f(selectionBox[0][0],selectionBox[0][1]);
			glVertex2f(selectionBox[0][0],selectionBox[1][1]);
			glVertex2f(selectionBox[1][0],selectionBox[1][1]);
			glVertex2f(selectionBox[1][0],selectionBox[0][1]);
		glEnd();
		glEndList();
		
		
	}
    void calculateExtents()
	{
        

		ratio = cast(float)size[0] / size[1];
        extents []= [ratio * viewFactor/zoom, viewFactor/zoom];
        
        updateBounds();

		//gameState.logger.info(L("upper = {} / {}",upper[0],upper[1]));

    }
	void zoomInOut(int direction)
	{
		zoom*=(1f+direction*0.1f);
		calculateExtents();
	}
	int on_key_press(int key,int mod)
	{
		
		switch (key)
		{
			case SDLK_PAGEUP:
				zoomInOut(2);
				return 1;
				break;
			case SDLK_PAGEDOWN:
				zoomInOut(-2);
				return 1;
				break;
			case SDLK_UP:
				viewCenter[1] -= (10f*0.01*zoom);
				updateBounds();
				return 1;
				
				break;
			case SDLK_DOWN:
				viewCenter[1] += (10f*0.01*zoom);
				updateBounds();
				return 1;
				break;
			default:
				break;
		}
		return 0;	
	}
	int on_mouse_move(int x,int y,int xrel,int yrel,int button,int shift,int ctrl,int alt)
	{
		//gameState.logger.warn(gameState.sprint("Button {} pressed",button));
		float[] pos=convertScreenToWorld([x, y]);
        if (pos==[-1f,-1f]) return 0;
            
        mouseWorld[]=pos;
		
		if ((button&SDL_BUTTON(SDL_BUTTON_RIGHT))||(button&SDL_BUTTON(SDL_BUTTON_LEFT)))
		{
			selectionType=(button&SDL_BUTTON(SDL_BUTTON_RIGHT))?SELECTION.RIGHT:SELECTION.LEFT;
			selectionBox[0][] = [min(pos[0],mouseWorldInitialPos[0]),
								min(pos[1],mouseWorldInitialPos[1])];
			selectionBox[1][] = [max(pos[0],mouseWorldInitialPos[0]),
								max(pos[1],mouseWorldInitialPos[1])];
			
			updateSelBoxDL();
			selBoxActive=true;
		}
		switch (button)
		{
			
			
			case SDL_BUTTON_MIDDLE:
				viewCenter[0] -= (xrel*0.01);
				viewCenter[1] -= (yrel*0.01*zoom);
				updateBounds();
				break;
				
			
			
			default:
				break;
		}
		return 1;
		
	}
	int on_mouse_release(int x,int y,int button,int shift,int ctrl,int alt)
	{
		float[] pos=convertScreenToWorld([x, y]);
        if (pos==[-1f,-1f]) return 0;
        if (selBoxActive)
		{
			selBoxActive=false;
			auto list=xGS.doSelectionRectangle(selectionBox);
			switch (button)
			{
				case SDL_BUTTON_LEFT:
				xGS.clearSelected();
				foreach (o;list)
				{
					xGS.selectActor(o,false);
				}
				break;
				case SDL_BUTTON_RIGHT:
					BaseObject target;
					if (list.size()>0)	target=cast(BaseObject)list.head();
					BehaviourData data;
					data.positions=new LinkedList!(float[])();
					data.positions.add(pos);
					if (target) data.target=target;
					data.shift=(shift==1);
					data.ctrl=(ctrl==1);
					data.alt=(alt==1);
					foreach (so;xGS.selectedObjects)
					{
						so.doAction(data);
						auto act=cast(BaseObject)so;
						Stdout.formatln("Action set for object {} with dest {}",act.name,data.positions.head());
					}
					
					break;
				default:
					break;
				
			}
		
			return 1;
		}
		return 0;
	}
	int on_mouse_press(int x,int y,int button,int shift,int ctrl,int alt)
	{
		float[] pos=convertScreenToWorld([x, y]);
        if (pos==[-1f,-1f]) return 0;
            
        mouseWorld[]=pos;

		if ((button&SDL_BUTTON(SDL_BUTTON_RIGHT))||(button&SDL_BUTTON(SDL_BUTTON_LEFT)))
		{
			
			mouseWorldInitialPos[]=pos[];
			selectionBox[0][]=pos[];
			selectionBox[1][]=pos[];
			updateSelBoxDL();
			selBoxActive=true;
		}
        switch (button)
		{
			case SDL_BUTTON_WHEELUP:
				zoomInOut(1);
				
				break;
			case SDL_BUTTON_WHEELDOWN:
				zoomInOut(-1);
				break;
			default:
				break;
		}
		return 1;
	}
    void updateBounds()
	{
        lower[] = viewCenter[]- extents[];
		upper[] = viewCenter[]+ extents[];
        viewOffset[0]=viewCenter[0]-cast(int)(size[0]/2);
		viewOffset[1]=viewCenter[1]-cast(int)(size[1]/2);
        //viewOffset = [a-b/2 for a,b in zip(viewCenter,size)]
	}
	void prepareView()
	{
        /*"""
        Updates the view offset based on the center of the screen.

        Recalculates the necessary projection.
        */
        
       
        glViewport(position[0],position[1], size[0],size[1]);

        glMatrixMode(GL_PROJECTION);
        glLoadIdentity();

        
        // L/R/B/T
        gluOrtho2D(lower[0], upper[0],lower[1], upper[1]);
		//gluOrtho2D(-2f,2f,-2f,2f);
        //print "(Resize) View extents", lower, upper, "Ratio", ratio
		// setup a perspective projection matrix
	    //gluPerspective(90f, 800f / 600f, .1, 100);

        
        glMatrixMode(GL_MODELVIEW);
        glLoadIdentity();
		
	}
	void draw(float dt)
	{
		prepareView();
		
		if (selBoxActive)
		{
			glPushAttrib(GL_LINE_STIPPLE|GL_BLEND|GL_COLOR_BUFFER_BIT);
			glEnable(GL_LINE_STIPPLE);
			glLineWidth(2);
			//glEnable(GL_BLEND);
			//glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
		
			glLineStipple(1,boxMask);
			glCallList(xenGS.DL.SelBox);
		
			glPopAttrib();
		}
		foreach (a;xGS.actors)
		{
			a.drawComponents(dt);
			
		}
		foreach (a;xGS.actors)
		{
			a.draw(dt);
			
		}
		foreach (a;xGS.beacons)
		{
			a.draw(dt);
			
		}
		
		foreach (icb;xGS.icebergs)
		{
			icb.draw(dt);
			
		}
	}
	float[] convertScreenToWorld(int[2] iPt)
	{
        /*"""
        Takes screen (x, y) and returns
        world coordinate b2Vec2(x',y').
        """*/
        float[] rel=getRelativeScreenCoord(iPt);
        if (rel==[-1f,-1f]) return rel;
        float u=rel[0];
		float v=rel[1];

        //p = box2d.b2Vec2()
		return [(1.0f - u) * lower[0] + u * upper[0],  (1.0f - v) * lower[1] + v * upper[1]];
        
        	}
	float[] convertWorldToScreen(float[2] p)
	{
        /*"""
        Takes world b2Vec2(x, y) and returns
        screen coordinate (x',y').
        """*/
        
		float[] tmp=new float[2];
		
		for (int i=0;i<2;i++) tmp[i]=(p[i]-lower[i])/(upper[i]-lower[i]);
        /*float u=(p[0]-self.lower[0])/(self.upper[0]-self.lower[0])
        float v=(p[1]-self.lower[1])/(self.upper[1]-self.lower[1])
		*/
        
		return [tmp[0]*size[0]+position[0], tmp[1]*size[1]+position[1]];
		
        
	}	
}
